package com.cgs.query.remote.netty

import com.dyuproject.protostuff.ProtostuffIOUtil
import com.dyuproject.protostuff.LinkedBuffer
import com.dyuproject.protostuff.Schema
import com.dyuproject.protostuff.runtime.RuntimeSchema
import org.hibernate.QueryException
import java.util.concurrent.ConcurrentHashMap
import org.springframework.objenesis.ObjenesisStd


interface Serialization {

    fun <T : Any> serialize(obj: T): ByteArray

    fun <T> deserialize(bytes: ByteArray, clazz: Class<T>): T
}

class ProtostuffSerializer : Serialization {

    companion object {
        private val objenesis = ObjenesisStd(true)
        private val cachedSchema = ConcurrentHashMap<Class<*>, Schema<*>>()
        private val buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)!!
    }

    private fun <T> getSchema(cls: Class<T>): Schema<T> {

        return cachedSchema.computeIfAbsent(cls) {
            RuntimeSchema.createFrom(cls)
        } as Schema<T>
    }

    override fun <T : Any> serialize(obj: T): ByteArray {
        val cls = obj.javaClass
        try {
            val schema = getSchema(cls)
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer)
        } catch (e: Exception) {
            throw QueryException("序列化失败", e)
        } finally {
            buffer.clear()
        }
    }

    override fun <T> deserialize(bytes: ByteArray, clazz: Class<T>): T {
        try {
            val message = objenesis.newInstance(clazz) as T
            val schema = getSchema(clazz)
            ProtostuffIOUtil.mergeFrom(bytes, message, schema)
            return message
        } catch (e: Exception) {
            throw QueryException("反序列化失败", e)
        }
    }
}